package ch.fusun.baron.map;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import ch.fusun.baron.data.DataListener;
import ch.fusun.baron.data.DataUpdate;
import ch.fusun.baron.map.api.GameMapService;
import ch.fusun.baron.map.api.GameMapUpdate;

/**
 * The basics for all game maps. Consists of {@link Tile}s to which other
 * components can reference.
 */
public class GameMap implements GameMapService {

	private int width = 1;

	private int height = 1;

	/**
	 * The tiles of the map
	 */
	private List<Tile> tiles;

	private transient final List<DataListener> listeners;

	/**
	 * Constructor
	 */
	public GameMap() {
		tiles = new ArrayList<Tile>();
		listeners = new LinkedList<DataListener>();
	}

	@Override
	public List<Tile> getTiles() {
		return this.tiles;
	}

	@Override
	public int getWidth() {
		return this.width;
	}

	@Override
	public int getHeight() {
		return this.height;
	}

	@Override
	public DataUpdate createFullUpdate() {
		return new GameMapUpdate(this.width, this.height);
	}

	@Override
	public void configure(int width, int height) {
		synchronized (this) {
			this.width = width;
			this.height = height;
			tiles = new ArrayList<Tile>();
			for (int i = 0; i < this.width; i++) {
				for (int j = 0; j < this.height; j++) {
					tiles.add(new Tile(i, j));
				}
			}
		}
		notifyListeners(new GameMapUpdate(this.width, this.height));
	}

	private void notifyListeners(DataUpdate update) {
		for (DataListener listener : listeners) {
			listener.dataChanged(update);
		}
	}

	@Override
	public void addDataListener(DataListener listener) {
		this.listeners.add(listener);
	}

	@Override
	public void removeDataListener(DataListener listener) {
		this.listeners.remove(listener);
	}

	@Override
	public Tile getTile(int x, int y) {
		synchronized (this) {

			if ((0 <= x && x < width) && (0 <= y && y < height)
					&& (tiles.size() > x * width + y)) {
				return tiles.get(x * width + y);
			}
			System.err.println("Access out of bounds: " + x + " " + y //$NON-NLS-1$ //$NON-NLS-2$
					+ " \t size is " + tiles.size()); //$NON-NLS-1$
			return null;
		}
	}

	@Override
	public Collection<? extends DataListener> getListeners() {
		return listeners;
	}

	@Override
	public void removeAllListeners() {
		this.listeners.clear();
	}

	@Override
	public void addDataListenerIfNotAlready(DataListener listener) {
		if (!listeners.contains(listener)) {
			addDataListener(listener);
		}
	}
}
